// -*- C++ -*-

//========================================================================
/**
 * Author   : cuisw <shaovie@gmail.com>
 * Date     : 2009-05-29 02:24
 */
//========================================================================

#ifndef NDK_EPOLL_REACTOR_H_
#define NDK_EPOLL_REACTOR_H_

#include "ndk/config.h"

#if defined NDK_LINUX

#include "ndk/pipe.h"
#include "ndk/strace.h"
#include "ndk/timer_queue.h"
#include "ndk/reactor_impl.h"
#include "ndk/reactor_token.h"

#include <signal.h>
#include <sys/epoll.h>

namespace ndk
{
  /**
   * @class epoll_event_tuple
   *
   * @brief Class that associates specific event mask with a given event 
   *        handler.
   */
  class epoll_event_tuple
  {
  public:
    epoll_event_tuple()
      : event_handler_(0),
      mask_(0)
    { }

    // The event handler
    event_handler *event_handler_;

    // The event mask for the above event handler.
    reactor_mask  mask_;
  };
  /**
   * class epoll_reactor_handler_repository
   * @brief
   * Used to map ndk_handles onto the appropriate event_handler *.
   *
   * This class is simply a container that maps a handle to its
   * corresponding event handler.  It is not meant for use outside of.
   * the epoll_reactor
   */
  class epoll_reactor_handler_repository
  {
  public:
    epoll_reactor_handler_repository()
      : max_size_(0),
      handlers_(0)
    { }

    // Initialize a repository of the appropriate size.
    int open(int size);

    // Close down the repository.
    int close(void);

    /**
     * Return the event_handler associated with ndk_handle.  If
     * index_p is non-zero, then return the index location of the handle, 
     * if found.
     */
    event_handler *find(ndk_handle handle, size_t *index_p = 0);

    // Bind the event_handler to handle with the appropriate reactor_mask 
    // setttings
    int bind(ndk_handle handle, event_handler *handler, reactor_mask mask);

    // Remove the binding for ndk_handle
    int unbind(ndk_handle handle);
    int unbind_all(void);

    // Check the handle to make sure it's a valid ndk_handle that is within 
    // the range of legal handles(i.e., greater than or equal to zero and 
    // less than max_size_).
    int is_invalid_handle(ndk_handle handle) const;

    // Check the handle to make sure it's a valid ndk_handle that is within 
    // the range of currently registered handles(i.e., greater than or 
    // equal to zero and less than max_handlep1_).
    int handle_in_range(ndk_handle handle) const;

    // Returns the current table size.
    size_t size(void) const;

    // Set the event mask for event handler associated with the given handle
    void mask(ndk_handle handle, reactor_mask mask); 

    // Retrieve the event mask for the event handler associated with the 
    // given handle.
    reactor_mask mask(ndk_handle handle);
  private:
    // Maximum number of handles.
    int max_size_;

    // The underlying array of event handlers.
    // The array of event handlers is directly indexed directly using 
    // an ndk_handle valie
    // This is Unix-specific.
    epoll_event_tuple *handlers_;
  }; // class epoll_reactor_handler_repository
  /**
   * @class epoll_reactor_notify
   *
   * @brief Event handler used for unblocking the epoll_reactor from its event 
   * loop.
   *
   * This event handler is used internally by the epoll_reactor as a means to 
   * allow a thread other then the one running the event loop to unblock the 
   * event loop.
   */
  class epoll_reactor;
  class epoll_reactor_notify : public reactor_notify
  {
  public:
    epoll_reactor_notify();
    // = Initialization and termination methods.
    virtual int open(reactor_impl *);

    virtual int close();

    /**
     * Called by a thread when it wants to unblock the <reactor_impl>.
     * This wakeups the <reactor_impl> if currently blocked.  Pass over
     * both the <event_handler> *and* the <mask> to allow the caller to
     * dictate which <event_handler> method the <reactor_impl> will invoke.
     */
    virtual int notify(event_handler *eh = 0,
                       reactor_mask mask = event_handler::except_mask);

    // Returns the ndk_handle of the notify pipe on which the reactor
    // is listening for notifications so that other threads can unblock
    // the <reactor_impl>.
    virtual ndk_handle notify_handle(void);

    // Read one of the notify call on the <handle> into the
    // <buffer>. This could be because of a thread trying to unblock
    // the <reactor_impl>
    virtual int read_notify_pipe(ndk_handle handle,
                                 notification_buffer &buffer);

    // Purge any notifications pending in this reactor for the specified
    // event_handler object. Returns the number of notifications purged. 
    // Returns -1 on error.
    virtual int purge_pending_notifications(event_handler * = 0,
                                            reactor_mask = event_handler::all_events_mask);

    // Handle readable event
    virtual int handle_input(ndk_handle handle);
  protected:
    // Keep a back pointer to the epoll_reactor. 
    epoll_reactor *ep_reactor_;

    /**
     * Contains the ndk_handle the epoll_reactor is listening on, as well as
     * the ndk_handle that threads wanting the attention of the epoll_reactor 
     * will write to.
     */
    pipe notification_pipe_;
  }; 
  /**
   * @class epoll_reactor
   *
   * @brief A `/dev/epoll' based reactor implemenatation.
   *
   * The epoll_reactor uses the '/dev/epoll' character devices to demultiplex 
   * events on a given set of file descriptors. Unlike select(), `/dev/epoll' 
   * have no hard-coded limit on the number of file descriptors that may be 
   * handled at any given time.  As such, the epoll_reactor can generally 
   * handle a much larger number of file descriptors than select() based 
   * reactors. Furthermore, since `/dev/epoll'return a set of file descriptors 
   * that are active, there is no need to "walk" the set of file descriptors 
   * to determine which ones are active, such as what is done with the select() 
   * system calls.  All returned file descriptors are active. This makes event 
   * dispatching very efficient.
   */
  typedef reactor_token epoll_reactor_token;
  class epoll_reactor : public reactor_impl
  {
  public:
    enum
    {
      add_mask       = 0x11,
      set_mask       = 0x12,
      clr_mask       = 0x13
    };
    epoll_reactor();

    virtual ~epoll_reactor();

    // Initialization
    /**
     * Initialize epoll_reactor with size.
     * Note On Unix platforms, the <size> parameter should be 
     * as large as the maximum number of file descriptors allowed 
     * for a given process.  This is necessary since a file descriptor
     * is used to directly index the array of event handlers
     * maintained by the Reactor's handler repository.  Direct
     * indexing is used for efficiency reasons.  If the size
     * parameter is less than the process maximum, the process
     * maximum will be decreased in order to prevent potential
     * access violations.
     * max_size_ whill be set to ::max_handles() if <size> equel to zero.
     */
    virtual int open(int size = 0, timer_queue *tq = 0, 
                     bool reset_fd_limit = true);

    // Close down and release all resources.
    virtual int close(void);

    // ++ Event loop drivers
    /**
     * This event loop driver blocks for up to @a max_wait_time before
     * returning.  It will return earlier if events occur.  Note that
     * @a max_wait_time can be -1, in which case this method blocks
     * indefinitely until events occur.
     *
     * return  The total number of event_handlers that were dispatched,
     * 0 if the @a max_wait_time elapsed without dispatching any handlers, 
     * or -1 if an error occurs.
     */
    virtual int handle_events(const time_value *max_wait_time = 0);

    // ++ Event handling control.
    /**
     * return The status of Reactor.  If this function returns 0, the
     * reactor is actively handling events.  If it returns non-zero, 
     * handle_events() return -1 immediately.
     */
    virtual int deactivated(void);

    /**
     * Control whether the Reactor will handle any more incoming events
     * or not.  If do_stop == 1, the Reactor will be disabled.  By
     * default, a reactor is in active state and can be deactivated/reactived 
     * as desired.
     */
    virtual void deactivate(int do_stop);

    // ++ Register and remove Handlers. These method maybe called in user thread 
    // context, so must use mutex
    /**
     * Register event_handler with mask.  The I/O handle will always come 
     * from handle on the event_handler.
     */
    virtual int register_handler(event_handler *eh,
                                 reactor_mask mask);

    /**
     * Register event_handler with mask.  The I/O handle is provided through 
     * the io_handle parameter.
     */
    virtual int register_handler(ndk_handle io_handle, 
                                 event_handler *eh,
                                 reactor_mask mask);

    /**
     * The I/O handle will be obtained using handle() method of event_handler.
     * If mask == event_handler::DONT_CALL then the handle_close() 
     * method of the event_handler is not invoked.
     */
    virtual int remove_handler(event_handler *eh,
                               reactor_mask mask);

    /**
     * Removes <handle>.  If <mask> == <event_handler::DONT_CALL then the 
     * handle_close() method of the associated <event_handler> is not invoked.
     */
    virtual int remove_handler(ndk_handle io_handle,
                               reactor_mask mask);

    // ++ Timer management.

    /**
     * Schedule a timer event.
     * Schedule a timer event that will expire after an <delay> amount
     * of time.  The return value of this method, a timer_id value,
     * uniquely identifies the <eh> in the Reactor's internal
     * list of timers.  This timer_id value can be used to cancel the 
     * timer with the cancel_timer() call. 
     */
    virtual int schedule_timer(event_handler *eh,
                               const void *arg,
                               const time_value &delay,
                               const time_value &interval = time_value::zero);

    // unix-like cron timer.
    virtual int crontab(const event_handler *eh, 
                        const void *arg, 
                        const char *entry);
    /**
     * Reset recurring timer interval.
     *
     * Resets the interval of the timer represented by  timer_id to
     * interval, which is specified in relative time to the current
     * <gettimeofday>.  If <interval> is equal to time_value::zero,
     * the timer will become a non-rescheduling timer.  Returns 0 
     * if successful, -1 if not.
     * This change will not take effect until the next timeout.
     */
    virtual int reset_timer_interval(int timer_id,
                                     const time_value &interval);

    /**
     * Cancel timer.
     *
     * Cancel timer associated with timer_id that was returned from
     * the schedule_timer() method.  If arg is non-NULL then it will be
     * set to point to the argument passed in when the handler was 
     * registered. This makes it possible to free up the memory and 
     * avoid memory leaks.  Returns 0 if cancellation succeeded and -1 
     * if the  timer_id wasn't found.
     *
     * On successful cancellation, event_handler::handle_close() will 
     * be called when <dont_call_handle_close> is zero, handle_close() 
     * will pass the argument passed in when the handler was 
     * registered, This makes it possible to release the memory of 
     * the <arg> in handle_close().
     * avoid memory leaks, also pass the argument <event_handler::TIMER_MASK>. 
     */
    virtual int cancel_timer(int timer_id,
                             const void **arg = 0,
                             int dont_call_handle_close = 1);

    /**
     * Cancel all timers associated with event handler.
     *
     * Shorthand for calling cancel_timer(long,const void **,int) 
     * multiple times for all timer associated with <eh>.
     *
     * event_handler::handle_close() will be called when 
     * <dont_call_handle_close> is zero, but not pass the argument 
     * passed in when the handler was registered, because it could 
     * register multiple argument<arg>, I don't know which should be
     * passed.
     *
     * Returns number of handlers cancelled.
     */
    virtual int cancel_timer(event_handler *eh,
                             int dont_call_handle_close = 1);

    // 
    virtual int notify(event_handler *eh = 0,
                       reactor_mask mask = event_handler::except_mask);

    // Purge any notifications pending in this reactor for the specified
    // event_handler object. Returns the number of notifications purged. 
    // Returns -1 on error.
    virtual int purge_pending_notifications(event_handler * = 0,
                                            reactor_mask = event_handler::all_events_mask);

    // Returns the current size of the Reactor's internal descriptor table
    virtual size_t size(void) const;

    // Get token.
    virtual token &lock();

    /**
     * Returns the actually number of the handles registered in Reactor.
     */
    virtual size_t curr_payload();

    // + Only the owner thread can perform a <handle_events>.

    // Set the new owner of the thread and return the old owner.
    virtual void owner(thread_t thr_id);

    // Return the current owner of the thread.
    virtual thread_t owner(void);

  protected:
    class token_guard;

    /**
     * Non-locking version of wait_pending().
     * Returns non-zero if there are I/O events "ready" for dispatching,
     * but does not actually dispatch the event handlers.  By default,
     * don't block while checking this. Return zero if timeout.
     */
    int work_pending_i(const time_value *max_wait_time);

    /**
     * Poll for events and return the number of event handlers that
     * were dispatched.This is a helper method called by all handle_events() 
     * methods.
     */
    int handle_events_i(const time_value *max_wait_time);

    /**
     * Perform the upcall with the given event handler method.
     */
    int upcall(event_handler *event_handler,
               int(event_handler::*callback)(ndk_handle),
               ndk_handle handle);

    /**
     * Dispatch event_handlers for time events, I/O events, Returns the 
     * total number of event_handlers that were dispatched or -1 if something 
     * goes wrong, 0 if no events ready.
     */
    int dispatch(void);

    /**
     * Dispatch all timers, if ready.
     * Returns: 0 if no timers ready(token still held),
     *		1 if a timer was expired(token released)
     */
    int dispatch_timer_handler(void);
    /**
     * Dispatch all IO events to the corresponding event handler.
     * Returns: 0 if no events ready ,
     *	       -1 on error
     *	       >0 the number of the ready events
     */
    int dispatch_io_event(void);

    /**
     * Register the given event handler with the reactor.
     */
    virtual int register_handler_i(ndk_handle handle,
                                   event_handler *event_handler,
                                   reactor_mask mask);

    // Remove the event handler which relate the handle
    int remove_handler_i(ndk_handle handle,
                         reactor_mask mask);

    // Convert a reactor mask to its corresponding epoll() event mask.
    virtual size_t reactor_mask_to_epoll_event(reactor_mask mask);

    // mask operate 
    int mask_opt_i(ndk_handle handle, 
                   reactor_mask mask, 
                   int opt);

    // increase payload
    void incre_payload(int n = 1);

    // decrease payload
    void decre_payload(int n = 1);
  protected:
    /**
     * @class token_guard
     * @brief A helper class that helps grabbing, releasing and waiting
     * on tokens for a thread that needs access to the reactor's token.
     */
    class token_guard
    {
    public:
      // Constructor that will grab the token for us
      token_guard(reactor_token &tk)
        : token_(tk),
        owner_(0)
      { }

      // Destructor. This will release the token if it hasn't been
      // released till this point
      ~token_guard(void) { this->release_token(); }

      // Release the token ..
      void release_token(void);

      // Returns whether the thread that created this object owns the
      // token or not.
      int is_owner(void) { return this->owner_;   }

      // A helper method that acquires the token 1) at a low priority
      // and wait quietly for the token, not waking another thread. This
      // is appropriate for cases where a thread wants to wait for and
      // dispatch an event, not causing an existing waiter to relinquish the
      // token, and also queueing up behind other threads waiting to modify
      // event records.
      int acquire_quietly(const time_value *timeout = 0);

      // A helper method that acquires the token at a high priority, and
      // does wake the current token holder.
      int acquire(const time_value *timeout = 0);  
    private:
      token_guard();
    private:
      // The Reactor token.
      epoll_reactor_token &token_;

      // Flag that indicate whether the thread that created this object
      // owns the token or not. A value of 0 indicates that this class
      // hasn't got the token(and hence the thread) and a value of 1
      // vice-versa.
      int owner_;
    };
    //
    bool initialized_;

    // The file descriptor associated with the open `/dev/epoll' device.
    int  epoll_fd_;

    // The maximum number of file descriptors over which demultiplexing will occur.
    int size_;

    // The actual number of the registed handlers
    size_t curr_payload_;
    thread_mutex curr_payload_mutex_;

    // This flag is used to keep track of whether we are actively handling
    // events or not.
    //AtomicOpt_T<int, ThreadMutex> deactivated_;
    sig_atomic_t deactivated_;

    // Keeps track of whether we should delete the timer queue(if we
    // didn't create it, then we don't delete it).
    int delete_timer_queue_;

    // Table of event structures to be filled by epoll_wait:
    struct epoll_event *events_;

    // Pointer to the next epoll_event array element that contains the next
    // event to be dispatched.
    struct epoll_event *start_pevents_;

    // The last element in the event array plus one.
    /**
     * The loop that dispatches IO events stops when this->start_pevents_ ==
     * this->end_pevents_.
     */
    struct epoll_event *end_pevents_;

    // Timer queue
    timer_queue *timer_queue_;

    // Callback object that unblocks the <epoll_reactor> if it's sleeping.
    reactor_notify *notify_handler_;

    // Current thread calls <handle_events>
    thread_t owner_;

    // The repository that contains all registered event handlers.
    epoll_reactor_handler_repository handler_rep_;

    // L/F Pattern mutex
    epoll_reactor_token   token_;
  };
} // namespace ndk
#include "ndk/epoll_reactor.inl"
#endif // NDK_LINUX
#endif // NDK_EPOLL_REACTOR_H_

